package cwb
import scala.util.parsing.combinator.RegexParsers
import scala.collection.mutable.LinkedList
import scala.util.parsing.combinator.JavaTokenParsers
import java.io.File
import java.io.PrintStream
import scala.collection.mutable.Stack

object QuitCommand extends Command {
  val name = "quit"
      val helpShort = "terminates the workbench session"
      val helpDetailed = "quit;"
      def apply(input: String) {
    println("Quitting: user said so")
    System.exit(0)
  }
}

object EchoCommand extends Command {
  val name = "echo"
      val helpShort = "print a string to whereever output's going "
      val helpDetailed = "echo \"foo\";" +
      "\nfoo is written to the current output file, or to the screen if there is no" +
      "\noutput file. This may be useful when running CWB scripts which produce a lot" +
      "\nof output."


  def apply(input: String) = {
    println(input.trim())
  }
}


object InputCommand extends Command {
  val name = "input"
      val helpShort = "execute the CWB commands in the given file"
      val helpDetailed = "input \"file\";" +
          "\n" +
          "\nThe file may contain any CWB commands (provided they do not require" +
          "\nuser interaction: e.g. you cannot use sim in an input file!) In" +
          "\nparticular, the file can itself contain input commands. If an error is" +
          "\nfound at any level, the CWB will stop processing input files and return to" +
          "\nstandard terminal input mode." +
          "\n\nThere must be quotes around the file name. The file name can be" +
          "\nabsolute or relative, but must not contain metacharacters like .."

  def apply(input: String) {
    // Remove all new line characters to allow for multiline commands
    InputParser.apply(input)
  }

  object InputParser extends JavaTokenParsers {
    def errorMsg(got: String) = "*** Expected: input \"file\". got input" + got + " ***" 
        val quote = "\""
        def accepted: Parser[String] = stringLiteral

        def commandString = accepted  ^^ {
        case filequotes => {
          // stringLiteral leaves quotes on string so lets remove these.
          val filename = filequotes.replace(quote, "")
              try {
                val source = io.Source.fromFile(filename)
                    val input = source.mkString
                    source.close()
                    val command_string = input.replace("\n", "")
                    val commands = command_string.split(";")
                    commands foreach(
                        // TODO(gustafa): Implement success boolean to allow aborting runs.
                        c => CWBCommands.runCommand(c)
                        )
              } catch {
              case e: Exception => println("File " + filename + " could not be read!")
              }
        }
    }

    def apply(input: String) {
      parseAll(commandString, input) match {
      case failure: NoSuccess => {
        println(errorMsg(input))
      }
      case _ =>
      }
    }
  }
}

object OutputCommand extends Command {
  val name = "output"
      val helpShort = "control where CWB output is written"
      val helpDetailed = "output \"file\";" +
          "\nsends output to the given file in future" +
          "\noutput" +
          "\n  sends output to stdout (e.g. the terminal) in future" +
          "\nfound at any level, the CWB will stop processing input files and return to" +
          "\nstandard terminal input mode." +
          "\n\nThere must be quotes around the file name. The file name can be" +
          "\nabsolute or relative, but must not contain metacharacters like .."

  private var current: (String, PrintStream) = ("stdout", System.out)
  private var outStack = new Stack[(String, PrintStream)]

      private def outTo(out: String) = System.out.println("Output goes to " + out)

      def apply(input: String) {
    // If no args simply redirect to stdout
    if (input.replace(" ", "").length == 0) {
      if (outStack.isEmpty) {
        current = ("stdout", System.out)
      } else {
        current = outStack.pop()
      }
    } else {
      // Read the filename.
      val start = input.indexOf("\"")
          val end = input.lastIndexOf("\"")
          var success = false
          if (end > start && input.count(c => c == '"') == 2) {
            val filename = input.slice(start + 1, end)
                if (filename.length != 0) {
                  try {
                    val ps = new PrintStream(filename)
                    Console.setOut(ps)
                    success = true
                    outStack.push(current)
                    current = (filename, ps)
                  } catch {
                  case _ => success = false
                  }
                }
          }

      if (!success) println(helpDetailed)
    }
    Console.setOut(current._2)
    outTo(current._1)
    // Remove all new line characters to allow for multiline commands
  }
}

object CWBCommand extends Command {
  val name = "cwb"
      val helpShort = "mostly for internal use: set CWB options "
      val helpDetailed = "\n" +
          "\ncwb option;" +
          "\nwhat this does depends on option" +
          "\n" +
          "\nMostly for developers' use; the one you might need to know about is" 


  def apply(input: String) {
    val sane = input.replace(" ", "")
        sane match {
        case "debug" => {
          cwb.toggleDebug()
          println("Debugging? " + cwb.debug)
        }
        case _ => println("cwb: Unknown option " + sane)
    }
  }
}


object HelpCommand extends Command {
  val name = "help"
      val helpShort = "provide on-line help"
      val helpDetailed = "help;" +
          "\nbrief help on what help is available" +
          "\nhelp commands;" + 
          "\nlist all commands, with one-line descriptions" +
          "\nhelp <command>;" +
          "\ngive help on the particular command" +
          "\n" +
          "\nSyntax of the process algebra, which used to be under help syntax, is now" +
          "\ngiven by a command with the name of the process algebra or ccs." +
          "\nSimilarly, to get help on the syntax of the logic, use command logic." +
          "\nThe old help brief has been removed, since it didn't seem very useful." + 
          "\n(Let me know what would be useful!)"
          val help_usage = "help commands;   - List of all commands, with one-line descriptions" +
              "\nhelp <command>;  - Help on each command, e.g. \"help quit;\""
              def apply(input: String) {
    parser.parseString(input)
  }

  object parser extends RegexParsers {
    val all = "commands"
        val specific = """[a-zA-Z0-9]+""".r
        def commands: Parser[Unit] = "commands" ^^ {
      _ => CWBCommands.showAvailableCommands()
    }
    def command: Parser[Unit] = specific ^^ {
    case lookup =>
    val com = CWBCommands.getCommand(lookup)
    com match {
    case Some(x: Command) => CWBCommands.printHelp(x)
    case _ => println("Sorry, no help on " + lookup)
    }
      }

      def acceptable = commands | command

          def parseString(input: String) {
        parseAll(acceptable, input) match {
        case failure: NoSuccess => {
          println(failure.msg)

          println("For on-line help, enter one of")
          CWBCommands.indentPrint(help_usage)
        }
        case _ =>
        }
      }
  }
}

object CoreComponent extends Module {
  def name = "CWB Core Component"
      def description = "The main component of the Edinburgh Concurrency Workbench"
      val commands = List(QuitCommand, HelpCommand, InputCommand, EqCommand, 
          StrongEqCommand, GraphCommand, SizeCommand, OutputCommand, CWBCommand,
          WGraphCommand, SimulationCommand, EchoCommand)
          def getCommands = commands

          def fst(pair: (Any, Any)) = pair._1

}


object CWBModules {

    private var _modules = new LinkedList[Module]

    def modules() = _modules
    def registerModule(m: Module) = {
    cwb.dbprintln("Registering module " + m.name)
    _modules = _modules ++ LinkedList(m)
   }

  /** 
   * registers the commands of all added modules to the CWB.
   */
  def registerCommands() {
    modules foreach {
      m => CWBCommands.registerCommands(m.getCommands)
    }
  }
}





